package com.h3bpm.starcharge.controller;

import java.io.File;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.h3bpm.base.engine.connection.Connection;
import com.h3bpm.base.engine.connection.EngineConnectionUtil;
import com.h3bpm.base.user.UserValidator;
import com.h3bpm.base.util.AppUtility;
import com.h3bpm.base.util.Sessions;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.NotImplementedException;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.alibaba.fastjson.JSONObject;

import OThinker.Common.RefObject;
import OThinker.Common.Clusterware.Connection.ConnectionResult;
import OThinker.Common.Clusterware.LogicUnits.AuthenticationType;
import OThinker.Common.Organization.Models.User;
import OThinker.Common.Organization.enums.UserServiceState;
import OThinker.H3.Controller.ControllerBase;
import OThinker.H3.Entity.IEngine;
import OThinker.H3.Entity.Site.PortalType;

import static org.apache.cxf.common.util.UrlUtils.urlEncode;

/**
 * 钉钉扫码登录控制器
 *
 * @author H3BPM
 * @date 2019/2/18
 */
@Controller
@RequestMapping("/starCharge/dindScanLogin")
public class DingScanLoginController extends ControllerBase {

    private static final Logger LOGGER = LoggerFactory.getLogger(DingScanLoginController.class);

    @Override
    public String getFunctionCode() {
        return null;
    }

    /**
     * 应用id
     */
    @Value("${DINGDING_APP_ID}")
    private String appId;
    /**
     * 应用secret
     */
    @Value("${DINGDING_APP_SECRET}")
    private String appSecret;
    /**
     * 企业corpid
     */
    @Value("${DINGDING_CORP_ID}")
    private String corpId;
    /**
     * 企业corpsecret
     */
    @Value("${DINGDING_CORP_SECRET}")
    private String corpSecret;

    /**
     * 获取token
     */
    @Value("${GET_TOKEN_URL}")
    private String getTokenUrl;
    /**
     * 获取企业token
     */
    @Value("${GET_APP_TOKEN_URL}")
    private String getAppTokenUrl;
    /**
     * 获取永久授权码
     */
    @Value("${GET_PERSISTENT_CODE_URL}")
    private String getPersistentCodeUrl;
    /**
     * 获取用户授权的SNS_TOKEN
     */
    @Value("${GET_SNS_TOKEN_URL}")
    private String getSnsTokenUrl;
    /**
     * 获取钉钉用户信息
     */
    @Value("${GET_USER_INFO_URL}")
    private String getUserInfoUrl;
    /**
     * 获取钉钉用户unionid获取userid
     */
    @Value("${GET_USERID_BY_UNIONID_URL}")
    private String getUserIdByUnionidUrl;
    /**
     * 获取用户数据
     */
    @Value("${GET_USER_DATA_URL}")
    private String getUserDataUrl;
    /**
     * 获取授权用户的个人信息
     */
    @Value("${GET_USER_INFO_BY_CODE}")
    private String getUserInfoByCode;

    /**
     * 钉钉扫码登录
     *
     * @param code
     * @param request
     * @param response
     * @return
     */
    @RequestMapping(value = "/doDingLogin", produces = "application/json; charset=UTF-8", method = RequestMethod.GET)
    @ResponseBody
    public JSONObject doDingLogin(String code, HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;
        try {
            // 获取授权用户的个人信息(unionid)
            String unionId = getUserInfo(code);
            // 获取accessToken
            String accessToken = getAccesstoken();
            // 根据unionId获取用户userId
            String userId = getUserId(accessToken, unionId);
            // 获取用户详细数据
            JSONObject userData = getUserData(accessToken, userId);

            String redirectUrl = "http://" + request.getServerName() + ":" + request.getServerPort();
            if (StringUtils.isEmpty(userId) || null == userData) {
                LOGGER.warn("钉钉组织架构不存在此用户。 unionId = " + unionId);
                response.sendRedirect(redirectUrl + "Portal/#/platform/login");
                return userData;
            }
            boolean isLogin = true;
            //验证用户是否存在H3BPM组织架构
            String userCode = AppUtility.getEngine().getOrganization().GetUserCodeByMobile(userData.getString("mobile"));
            User user = AppUtility.getEngine().getOrganization().GetUserByCode(userCode);
            if (user != null) {
                UserValidator userValidator = this.getUserValidator(user, request);
                if (userValidator != null) {
                    // 当前用户登录
                    request.getSession().setMaxInactiveInterval(60 * 60);
                    request.getSession().setAttribute(Sessions.GetUserValidator(), userValidator);
                    AppUtility.OnUserLogin(userValidator, request, PortalType.UserPortal);
                    response.setHeader("Access-Control-Allow-Credentials", "true");
                    //获取当前登录用户
                    User currentUser = this.getUserValidator().getUser();
                    if (currentUser != null) {
                        //成功直接跳转待办页面
                        response.sendRedirect(redirectUrl + "/Portal/#/app/Workflow/MyUnfinishedWorkItem");
                    } else {
                        LOGGER.warn("用户验证失败, 请稍后重试。");
                        isLogin = false;
                    }
                } else {
                    LOGGER.warn("用户验证失败, 请稍后重试。");
                    isLogin = false;
                }
            } else {
                LOGGER.warn("H3BPM组织架构不存在此用户。 userCode = " + userData.getString("mobile"));
                isLogin = false;
            }
            if (!isLogin) {
                response.sendRedirect(redirectUrl + "/Portal/#/platform/login");
            }
        } catch (Exception e) {
            LOGGER.error(">>Msg.", e);
        }
        return null;
    }

    /**
     * 获取Token
     *
     * @return String
     */
    private String getAccesstoken() {
        return getToken();
    }

    /**
     * 获取企业Token
     *
     * @return String
     */
    private String getAppAccesstoken() {
        return getToken();
    }

    private String getToken() {
        String url = getAppTokenUrl;
        JSONObject json = ossHttpGetUtil(url);
        if (null != json) {
            if (Integer.parseInt(json.get("errcode").toString()) == 0) {
                String appAccessToken = json.getString("access_token");
                return appAccessToken;
            }
        }
        return "";
    }

    /**
     * 获取永久授权码
     *
     * @param accessToken
     * @param code
     * @return JSONObject
     */
    private JSONObject getPersistentCode(String accessToken, String code) {
        String url = String.format(getPersistentCodeUrl, accessToken);
        JSONObject jsonData = new JSONObject();
        jsonData.put("tmp_auth_code", code);
        JSONObject json = ossHttpPostUtil(url, jsonData);
        if (null != json) {
            if (Integer.parseInt(json.get("errcode").toString()) == 0) {
                return json;
            }
        }
        return null;
    }

    /**
     * 获SnsToken
     *
     * @param accessToken
     * @param openid
     * @param persistentCode
     * @return String
     */
    private String getSnsToken(String accessToken, String openid, String persistentCode) {
        String url = String.format(getSnsTokenUrl, accessToken);
        JSONObject jsonData = new JSONObject();
        jsonData.put("openid", openid);
        jsonData.put("persistent_code", persistentCode);
        JSONObject json = ossHttpPostUtil(url, jsonData);
        if (null != json) {
            if (Integer.parseInt(json.get("errcode").toString()) == 0) {
                return json.getString("sns_token");
            }
        }
        return null;
    }

    /**
     * 获取用户unionId
     * @param code
     * @return
     * @throws Exception
     */
    private String getUserInfo(String code) throws Exception {
        long l = System.currentTimeMillis();
        String timestamp = String.valueOf(l);
        String url = String.format(getUserInfoByCode, getSignature(timestamp), timestamp, appId);
        JSONObject jsonData = new JSONObject();
        jsonData.put("tmp_auth_code", code);
        JSONObject json = ossHttpPostUtil(url, jsonData);

        if (null != json) {
            if (Integer.parseInt(json.get("errcode").toString()) == 0) {
                JSONObject jsonUser = json.getJSONObject("user_info");
                return jsonUser.getString("unionid");
            }
        }
        return null;
    }

    /**
     * 根据timestamp, appSecret计算签名值
     * @param timestamp
     * @return
     * @throws Exception
     */
    private String getSignature (String timestamp) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(new SecretKeySpec(appSecret.getBytes("UTF-8"), "HmacSHA256"));
        byte[] signatureBytes = mac.doFinal(timestamp.getBytes("UTF-8"));
        String signature = new String(Base64.encodeBase64(signatureBytes));
        return urlEncode(signature);
    }

    /**
     * 获取unionId
     *
     * @param snsToken
     * @return String
     */
    private String getUnionId(String snsToken) {
        String url = String.format(getUserInfoUrl, snsToken);
        JSONObject json = ossHttpGetUtil(url);
        if (null != json) {
            if (Integer.parseInt(json.get("errcode").toString()) == 0) {
                JSONObject jsonUser = json.getJSONObject("user_info");
                String unionid = jsonUser.getString("unionid");
                return unionid;
            }
        }
        return "";
    }

    /**
     * 获取用户id
     *
     * @param accessToken
     * @param unionId
     * @return String
     */
    private String getUserId(String accessToken, String unionId) {
        String url = String.format(getUserIdByUnionidUrl, unionId, accessToken);
        JSONObject json = ossHttpGetUtil(url);
        if (null != json) {
            if (Integer.parseInt(json.get("errcode").toString()) == 0) {
                return json.getString("userid");
            }
        }
        return "";
    }

    /**
     * 获取用户数据
     *
     * @param accessToken
     * @param userId
     * @return JSONObject
     */
    private JSONObject getUserData(String accessToken, String userId) {
        String url = String.format(getUserDataUrl, accessToken, userId);
        JSONObject json = ossHttpGetUtil(url);
        if (null != json) {
            if (Integer.parseInt(json.get("errcode").toString()) == 0) {
                return json;
            }
        }
        return null;
    }

    /**
     * 用户验证
     *
     * @param user
     * @param request
     * @return UserValidator
     */
    private UserValidator getUserValidator(User user, HttpServletRequest request) {
        UserValidator userValidator = null;
        String tempImagesPath = System.getProperty("user.dir") + File.separator + "TempImages";

        try {
            IEngine engine = AppUtility.getEngine();

            switch (EngineConnectionUtil.getConnectionMode()) {
                case Mono:
                    if (user == null || user.getServiceState() == UserServiceState.Dismissed || user.getIsVirtualUser()) {
                        return null;
                    }
                    userValidator = new UserValidator(request, engine, user.getObjectID(), tempImagesPath, null);
                    break;
                case Shared:
                    Connection conn = new Connection();
                    String userId = "";

                    RefObject<String> tempRefUserId = new RefObject<>(userId);
                    boolean tempVar = conn.Open(EngineConnectionUtil.getConnectionStringParser().getServers()[0],
                            AuthenticationType.WeChat, engine.getEngineConfig().getCode(), user.getObjectID(),
                            "DingTalk") != ConnectionResult.Success;
                    userId = tempRefUserId.argvalue;
                    if (tempVar) {
                        return null;
                    }
                    userValidator = new UserValidator(request, conn.getEngine(), userId, tempImagesPath, null);
                    break;
                default:
                    throw new NotImplementedException();
            }
        } catch (Exception e) {
            LOGGER.error(">>Msg.", e);
        }

        return userValidator;
    }

    /**
     * 封装http GET 请求
     *
     * @param url
     * @return JSONObject
     */
    private JSONObject ossHttpGetUtil(String url) {
        HttpGet httpGet = new HttpGet(url);
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        HttpClient httpClient = httpClientBuilder.build();
        HttpResponse httpResponse = null;
        try {
            httpResponse = httpClient.execute(httpGet);
        } catch (Exception e) {
            LOGGER.error(">>Msg.", e);
        }
        // 得到httpResponse的状态响应码
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        JSONObject jsonObject = null;
        if (statusCode == HttpStatus.SC_OK) {
            // 得到httpResponse的实体数据
            HttpEntity httpEntity = httpResponse.getEntity();
            if (httpEntity != null) {
                try {
                    jsonObject = JSONObject.parseObject(EntityUtils.toString(httpEntity));
                    return jsonObject;
                } catch (Exception e) {
                    LOGGER.error(">>Msg.", e);
                }
            }
        }
        return null;
    }

    /**
     * 封装http POST请求
     *
     * @param url
     * @param json
     * @return JSONObject
     */
    private JSONObject ossHttpPostUtil(String url, JSONObject json) {
        HttpPost httpPost = new HttpPost(url);
        HttpEntity httpEntity;
        httpEntity = new StringEntity(json.toString(), "UTF-8");
        httpPost.setEntity(httpEntity);
        HttpResponse httpResponse = null;
        HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
        HttpClient httpClient = httpClientBuilder.build();
        try {
            httpResponse = httpClient.execute(httpPost);
        } catch (Exception e) {
            LOGGER.error(">>Msg.", e);
        }
        // 得到httpResponse的状态响应码
        int statusCode = httpResponse.getStatusLine().getStatusCode();
        if (statusCode == HttpStatus.SC_OK) {
            // 得到httpResponse的实体数据
            HttpEntity httpEntity2 = httpResponse.getEntity();
            if (httpEntity2 != null) {
                try {
                    return JSONObject.parseObject(EntityUtils.toString(httpEntity2));
                } catch (Exception e) {
                    LOGGER.error(">>Msg.", e);
                }
            }
        }
        return null;
    }
}
